﻿using System.Collections;
using System.Collections.Specialized;
using System.Web.Caching;
using System.Xml;
using System.Configuration;
using System.Web;
using Whir.Common;
using System.Text.RegularExpressions;
using System;

namespace Whir.Common.Resource
{
    /// <summary>
    /// 资源管理类
    /// 用于读取相应语言的资源文本
    /// </summary>
    public class ResourceManager
    {
        /// <summary>
        /// 站点默认语言
        /// </summary>
        private static string defaultLanguage;

        /// <summary>
        /// 获取站点当前语言名称
        /// </summary>
        public static string SiteLanguageName
        {
            get { return GetSupportedLanguage(defaultLanguage); }
        }
        /// <summary>
        /// 获取或设置站点当前语言的键值
        /// </summary>
        public static string SiteLanguageKey
        {
            get { return defaultLanguage; }
            set { ResourceManager.defaultLanguage = value; }
        }


        enum ResourceType
        {
            String,
            ErrorMessage,
            Template
        }

        /// <summary>
        /// 静态构造,用于实例化上下文引用对象
        /// </summary>
        static ResourceManager()
        {
            defaultLanguage = Settings.GetString("defaultLanguage");
        }

        /// <summary>
        /// 从语言声明文件中获取支持的所有语言
        /// </summary>
        /// <returns>支持的所有语言的名称-值对</returns>
        public static NameValueCollection GetSupportedLanguages()
        {
            HttpContext csContext = HttpContext.Current;
            string cacheKey = "SupportedLanguages";

            NameValueCollection supportedLanguages = ResourceManagerCache.Get(cacheKey) as NameValueCollection;
            if (supportedLanguages == null)
            {
                string filePath = csContext.Server.MapPath(Settings.GetString("languages") + "languages.xml");
                CacheDependency dp = new CacheDependency(filePath);
                supportedLanguages = new NameValueCollection();

                XmlDocument d = new XmlDocument();
                d.Load(filePath);

                foreach (XmlNode n in d.SelectSingleNode("root").ChildNodes)
                {
                    if (n.NodeType != XmlNodeType.Comment)
                    {
                        supportedLanguages.Add(n.Attributes["key"].Value, n.Attributes["name"].Value);
                    }
                }

                ResourceManagerCache.Max(cacheKey, supportedLanguages, dp);
            }

            return supportedLanguages;
        }

        /// <summary>
        /// 获取语言字符集
        /// </summary>
        /// <returns></returns>
        public static NameValueCollection GetLanguageCharsets()
        {
            HttpContext csContext = HttpContext.Current;
            string cacheKey = "LanguageCharsets";

            NameValueCollection languageCharsets = ResourceManagerCache.Get(cacheKey) as NameValueCollection;
            if (languageCharsets == null)
            {
                string filePath = csContext.Server.MapPath(Settings.GetString("languages") + "languages.xml");
                CacheDependency dp = new CacheDependency(filePath);
                languageCharsets = new NameValueCollection();

                XmlDocument d = new XmlDocument();
                d.Load(filePath);

                foreach (XmlNode n in d.SelectSingleNode("root").ChildNodes)
                {
                    if (n.NodeType != XmlNodeType.Comment)
                    {
                        if (n.Attributes["emailCharset"] == null)
                            continue;
                        string codepage = n.Attributes["emailCharset"].Value;
                        if (n.Attributes["emailSubjectCharset"] != null)
                            codepage += "," + n.Attributes["emailSubjectCharset"].Value;

                        languageCharsets.Add(n.Attributes["key"].Value, codepage);
                    }
                }

                ResourceManagerCache.Max(cacheKey, languageCharsets, dp);
            }

            return languageCharsets;
        }

        /// <summary>
        /// 根据语言名称获取语言值
        /// 如果不能获取,则使用默认语言
        /// </summary>
        /// <param name="language">语言名称</param>
        /// <returns>语言值</returns>
        public static string GetSupportedLanguage(string language)
        {
            string defaultLanguage = ResourceManager.defaultLanguage; //默认语言
            NameValueCollection supportedLanguages = GetSupportedLanguages();
            string supportedLanguage = supportedLanguages[language];

            if (!string.IsNullOrEmpty(supportedLanguage))
                return language;
            else
                return defaultLanguage;
        }

        /// <summary>
        /// 根据资源名称从资源文件中获取相应资源文本
        /// </summary>
        /// <param name="name">资源名称</param>
        /// <returns>资源文本</returns>
        public static string GetString(string name)
        {
            return GetString(name, false);
        }

        /// <summary>
        /// 根据资源名称从资源文件中获取相应资源文本
        /// </summary>
        /// <param name="name">资源名称</param>
        /// <param name="defaultOnly">只选择默认语言</param>
        /// <returns>资源文本</returns>
        public static string GetString(string name, bool defaultOnly)
        {
            return GetString(name, "Resources.xml", defaultOnly);
        }

        /// <summary>
        /// 根据资源名称从资源文件中获取相应资源文本
        /// </summary>
        /// <param name="name">资源名称</param>
        /// <param name="fileName">资源文件名</param>
        /// <returns></returns>
        public static string GetString(string name, string fileName)
        {
            return GetString(name, fileName, false);
        }

        /// <summary>
        /// 根据资源名称从资源文件中获取相应资源文本
        /// </summary>
        /// <param name="name">资源名称</param>
        /// <param name="fileName">资源文件名</param>
        /// <param name="defaultOnly">使用默认语言</param>
        /// <returns>资源文本</returns>
        public static string GetString(string name, string fileName, bool defaultOnly)
        {
            HttpContext csContext = HttpContext.Current;
            //使用HashTable装载所有资源
            Hashtable resources = null;
            //用户语言
            string userLanguage;
            //csContext.Items["AspSession"];
            //if (csContext.Request.Cookies["userLanguage"] != null)
            //    userLanguage = csContext.Request.Cookies["userLanguage"].Value;
            //else
            //    userLanguage = null;
            if (csContext.Session[Settings.GetString("languageSessionKey")] != null)
                userLanguage = csContext.Session[Settings.GetString("languageSessionKey")].ToString();
            else
                userLanguage = null;



            if (fileName != null && fileName != "")
                resources = GetResource(ResourceType.String, userLanguage, fileName, defaultOnly);
            else
                resources = GetResource(ResourceType.String, userLanguage, "Resources.xml", defaultOnly);

            string text = resources[name] as string;

            //try the standard file if we passed a file that didnt have the key we were looking for
            if (text == null && fileName != null && fileName != "")
            {
                resources = GetResource(ResourceType.String, userLanguage, "Resources.xml", false);

                text = resources[name] as string;
            }

            return text;
        }

        /// <summary>
        /// 获取资源
        /// </summary>
        /// <param name="resourceType">资源类型</param>
        /// <param name="userLanguage">用户语言</param>
        /// <param name="fileName">资源文件名</param>
        /// <param name="defaultOnly">只使用默认语言</param>
        /// <returns></returns>
        private static Hashtable GetResource(ResourceType resourceType, string userLanguage, string fileName, bool defaultOnly)
        {
            string defaultLanguage = ResourceManager.defaultLanguage;
            string cacheKey = resourceType.ToString() + defaultLanguage + userLanguage + fileName;

            // 如果用户没有定制语言,则使用默认
            //
            if (string.IsNullOrEmpty(userLanguage) || defaultOnly)
                userLanguage = defaultLanguage;

            // 从缓存中获取资源
            //
            Hashtable resources = ResourceManagerCache.Get(cacheKey) as Hashtable;

            if (resources == null)
            {
                resources = new Hashtable();

                resources = LoadResource(resourceType, resources, defaultLanguage, cacheKey, fileName);

                // 如果用户设置了语言则加载用户语言资源
                //
                if (defaultLanguage != userLanguage)
                    resources = LoadResource(resourceType, resources, userLanguage, cacheKey, fileName);

            }

            return resources;
        }

        /// <summary>
        /// 加载资源
        /// </summary>
        /// <param name="resourceType"></param>
        /// <param name="target"></param>
        /// <param name="language"></param>
        /// <param name="cacheKey"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private static Hashtable LoadResource(ResourceType resourceType, Hashtable target, string language, string cacheKey, string fileName)
        {
            HttpContext csContext = HttpContext.Current;

            string filePath = csContext.Server.MapPath(Settings.GetString("languages") + language + "/" + fileName);

            CacheDependency dp = new CacheDependency(filePath);

            XmlDocument d = new XmlDocument();
            try
            {
                d.Load(filePath);
            }
            catch
            {
                return target;
            }

            foreach (XmlNode n in d.SelectSingleNode("root").ChildNodes)
            {
                if (n.NodeType != XmlNodeType.Comment)
                {
                    if (target[n.Attributes["name"].Value] == null)
                        target.Add(n.Attributes["name"].Value, n.InnerText);
                    else
                        target[n.Attributes["name"].Value] = n.InnerText;
                }

            }

            if (language == ResourceManager.defaultLanguage)
            { ResourceManagerCache.Max(cacheKey, target, dp); }
            else
            { ResourceManagerCache.Insert(cacheKey, target, dp, ResourceManagerCache.MinuteFactor * 5); }

            return target;
        }
    }

    /// <summary>
    /// 缓存管理
    /// </summary>
    public class ResourceManagerCache
    {
        private ResourceManagerCache() { }

        //>> Based on Factor = 5 default value
        public static readonly int DayFactor = 17280;
        public static readonly int HourFactor = 720;
        public static readonly int MinuteFactor = 12;
        public static readonly double SecondFactor = 0.2;

        private static readonly Cache _cache;

        private static int Factor = 5;

        public static void ReSetFactor(int cacheFactor)
        {
            Factor = cacheFactor;
        }

        /// <summary>
        /// Static initializer should ensure we only have to look up the current cache
        /// instance once.
        /// </summary>
        static ResourceManagerCache()
        {
            HttpContext context = HttpContext.Current;
            if (context != null)
            {
                _cache = context.Cache;
            }
            else
            {
                _cache = HttpRuntime.Cache;
            }
        }

        /// <summary>
        /// 清空Cash对象
        /// </summary>
        public static void Clear()
        {
            IDictionaryEnumerator CacheEnum = _cache.GetEnumerator();
            ArrayList al = new ArrayList();
            while (CacheEnum.MoveNext())
            {
                al.Add(CacheEnum.Key);
            }

            foreach (string key in al)
            {
                _cache.Remove(key);
            }

        }

        /// <summary>
        /// 根据正则表达式的模式移除Cache
        /// </summary>
        /// <param name="pattern">模式</param>
        public static void RemoveByPattern(string pattern)
        {
            IDictionaryEnumerator CacheEnum = _cache.GetEnumerator();
            Regex regex = new Regex(pattern, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled);
            while (CacheEnum.MoveNext())
            {
                if (regex.IsMatch(CacheEnum.Key.ToString()))
                    _cache.Remove(CacheEnum.Key.ToString());
            }
        }

        /// <summary>
        /// 根据键值移除Cache
        /// </summary>
        /// <param name="key"></param>
        public static void Remove(string key)
        {
            _cache.Remove(key);
        }

        /// <summary>
        /// 把对象加载到Cache
        /// </summary>
        /// <param name="key">键</param>
        /// <param name="obj">对象</param>
        public static void Insert(string key, object obj)
        {
            Insert(key, obj, null, 1);
        }

        /// <summary>
        /// 把对象加载到Cache,附加缓存依赖信息
        /// </summary>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        /// <param name="dep"></param>
        public static void Insert(string key, object obj, CacheDependency dep)
        {
            Insert(key, obj, dep, MinuteFactor * 3);
        }

        /// <summary>
        /// 把对象加载到Cache,附加过期时间信息
        /// </summary>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        /// <param name="seconds"></param>
        public static void Insert(string key, object obj, int seconds)
        {
            Insert(key, obj, null, seconds);
        }

        /// <summary>
        /// 把对象加载到Cache,附加过期时间信息和优先级
        /// </summary>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        /// <param name="seconds"></param>
        /// <param name="priority"></param>
        public static void Insert(string key, object obj, int seconds, CacheItemPriority priority)
        {
            Insert(key, obj, null, seconds, priority);
        }

        /// <summary>
        /// 把对象加载到Cache,附加缓存依赖和过期时间(多少秒后过期)
        /// (默认优先级为Normal)
        /// </summary>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        /// <param name="dep"></param>
        /// <param name="seconds"></param>
        public static void Insert(string key, object obj, CacheDependency dep, int seconds)
        {
            Insert(key, obj, dep, seconds, CacheItemPriority.Normal);
        }

        /// <summary>
        /// 把对象加载到Cache,附加缓存依赖和过期时间(多少秒后过期)及优先级
        /// </summary>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        /// <param name="dep"></param>
        /// <param name="seconds"></param>
        /// <param name="priority"></param>
        public static void Insert(string key, object obj, CacheDependency dep, int seconds, CacheItemPriority priority)
        {
            if (obj != null)
            {
                _cache.Insert(key, obj, dep, DateTime.Now.AddSeconds(Factor * seconds), TimeSpan.Zero, priority, null);
            }

        }

        /// <summary>
        /// 把对象加到缓存并忽略优先级
        /// </summary>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        /// <param name="secondFactor"></param>
        public static void MicroInsert(string key, object obj, int secondFactor)
        {
            if (obj != null)
            {
                _cache.Insert(key, obj, null, DateTime.Now.AddSeconds(Factor * secondFactor), TimeSpan.Zero);
            }
        }

        /// <summary>
        /// 把对象加到缓存,并把过期时间设为最大值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        public static void Max(string key, object obj)
        {
            Max(key, obj, null);
        }

        /// <summary>
        /// 把对象加到缓存,并把过期时间设为最大值,附加缓存依赖信息
        /// </summary>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        /// <param name="dep"></param>
        public static void Max(string key, object obj, CacheDependency dep)
        {
            if (obj != null)
            {
                _cache.Insert(key, obj, dep, DateTime.MaxValue, TimeSpan.Zero, CacheItemPriority.AboveNormal, null);
            }
        }

        /// <summary>
        /// 插入持久性缓存
        /// </summary>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        public static void Permanent(string key, object obj)
        {
            Permanent(key, obj, null);
        }

        /// <summary>
        /// 插入持久性缓存,附加缓存依赖
        /// </summary>
        /// <param name="key"></param>
        /// <param name="obj"></param>
        /// <param name="dep"></param>
        public static void Permanent(string key, object obj, CacheDependency dep)
        {
            if (obj != null)
            {
                _cache.Insert(key, obj, dep, DateTime.MaxValue, TimeSpan.Zero, CacheItemPriority.NotRemovable, null);
            }
        }

        /// <summary>
        /// 根据键获取被缓存的对象
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static object Get(string key)
        {
            return _cache[key];
        }

        /// <summary>
        /// Return int of seconds * SecondFactor
        /// </summary>
        public static int SecondFactorCalculate(int seconds)
        {
            // Insert method below takes integer seconds, so we have to round any fractional values
            return Convert.ToInt32(Math.Round((double)seconds * SecondFactor));
        }
    }
}